package com.dudy;


import java.math.BigDecimal;

/**
 * author:zjw20
 * date:2016/9/6
 * email:zjw2023@163.com
 * description:装饰模式适用于对现有的类进行扩展，能最大限度不修改原有类
 * 扩展类（装饰类）、被装饰类必须继承同一个接口或者同一个抽象类
 * 装饰模式其实就是在当前类的外面增加一层装饰，为当前类的功能进行扩展，如spring的事务处理，就是在service外增加一层包装，增加事务管理
 */
public class 装饰模式 {
    public static void main(String[] args) {
        BlackCoffee blackCoffee = new BlackCoffee();
        Mocha mocha = new Mocha(blackCoffee);
        System.out.println(mocha.getDescription());
        System.out.println(mocha.cost());
    }
}

/**
 * 以咖啡为例，对现有咖啡进行扩展，并给出新咖啡的价格
 */
abstract class Coffee{
    /**
     * 咖啡种类
     */
    public String description = "unknown coffee";
    public String getDescription(){
        return description;
    }

    /**
     * 咖啡的价格
     * @return
     */
    public abstract double cost();
}

/**
 * 黑咖啡
 */
class BlackCoffee extends Coffee{
    public BlackCoffee() {
        description = "black coffee";
    }

    @Override
    public double cost() {
        return 2.2;
    }
}

/**
 * 白咖啡
 */
class WhiteCoffee extends Coffee{
    public WhiteCoffee() {
        description = "white coffee";
    }

    @Override
    public double cost() {
        return 3.2;
    }
}

/**
 * 修饰类，咖啡的调料包，比如牛奶、巧克力等
 */
abstract class CondimentDecorator extends Coffee{
    //强制所有的继承泪实现该方法，主要是描述当前的coffee是那种类型
    @Override
    public abstract String getDescription();
}

/**
 * 修饰类包含了基础类，也就是说修饰类就是在基础类外面增加了一层装饰，由于他们继承同一个接口或者抽象类，那么基础的部分是可以公用的
 */
class Mocha extends CondimentDecorator{
    private Coffee coffee;

    public Mocha(Coffee coffee) {
        this.coffee = coffee;
    }

    /**
     * 计算mocha咖啡的总价，基础咖啡价格（blackCoffee或者WhiteCoffee）+Mocha调料包（如牛奶）价格
     * @return
     */
    public double cost() {
        return MathUtils.add(coffee.cost(),0.2);
    }

    public String getDescription() {
        return coffee.getDescription()+", Mocha";
    }
}

/**
 * 由于double不能进行精准的运算，因此这里需要转换成bigDecimal，然后再转换为double
 */
class MathUtils{
    private MathUtils(){}
    public static double add(double v1,double v2){
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }
}

